#pragma once

#include <string>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <functional>
#include <map>
using namespace std;
using namespace std::placeholders;

#include <xercesc/sax2/Attributes.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/framework/XMLFormatter.hpp>

XERCES_CPP_NAMESPACE_USE

#define MAX_XML_STRING_LENGTH   512
#define MAX_XML_PATH_LENGTH     128
#define XML_PATH_DEF_DELIMITER  '/'

#define XERCES_XML_NODE_TYPE_STRUCT  0
#define XERCES_XML_NODE_TYPE_VALUE   1

#define XML_ENCODEING_GBK2312  "GB2312"
#define XML_ENCODEING_GBK      "GBK"
#define XML_ENCODEING_UTF8     "UTF-8"
#define XML_ENCODEING_UTF16    "UTF-16"
#define XML_ENCODEING_DEFAULT  XML_ENCODEING_GBK2312


class MyXmlFormatTarget;
class SAX2ReadXpath;
class SAX2ReadHandler;
class CSAX2Parser;
class XercesXmlNode;

typedef std::function<void (XercesXmlNode& )> XercesXmlHandle_t;
	
class XercesXmlNode
{
	private:
		uint16_t fXmlNodeType;
		string fXmlNodeName;
		string fXmlNodePath;
		string fXmlNodeValue;
		std::map<string,string> fXmlNodeAttrs;
			
	public:
		XercesXmlNode()
			: fXmlNodeType(XERCES_XML_NODE_TYPE_STRUCT)
		{ 
		};
		
		XercesXmlNode(uint16_t nType,const string& name,const string& path)
			: fXmlNodeType(nType)
			, fXmlNodeName(name)
			, fXmlNodePath(path)
		{
			
		};
		
		virtual ~XercesXmlNode(){};		
		
		inline uint16_t getXmlNodeType() { return fXmlNodeType; };
		inline string&  getXmlNodeName() { return fXmlNodeName; };
		inline string&  getXmlNodePath() { return fXmlNodePath; };
		inline string&  getXmlNodeValue(){ return fXmlNodeValue;};
		
		inline std::map<string,string>& getXmlNodeAttrs() { return fXmlNodeAttrs; };
		inline size_t getAttrsSize() { return fXmlNodeAttrs.size(); };
		
		inline void setXmlNodeType(uint16_t nType) { fXmlNodeType = nType; };
		inline void setXmlNodeName(const string& name) { fXmlNodeName = name;};
		inline void setXmlNodeValue(const string& value) { fXmlNodeValue = value; };
		inline void setXmlNodePath(const string& path) { fXmlNodePath = path; };
		inline void setXmlNodeAttrs(const string& xmlAttr_name,const string& xmlAttr_value)
        { 
            if(xmlAttr_name.length() > 0) 
                fXmlNodeAttrs[xmlAttr_name] = xmlAttr_value;
        };

        void clear()
        {
            fXmlNodeName.clear();
            fXmlNodePath.clear();
            fXmlNodeValue.clear();
            fXmlNodeAttrs.clear();
        };
};

class MyXmlFormatTarget : public XMLFormatTarget
{
public:
    MyXmlFormatTarget() {};
    ~MyXmlFormatTarget () {};
    
    const char * getBuffer();
    virtual void writeChars(const XMLByte* const toWrite,  const XMLSize_t count,XMLFormatter * const formatter);
    
private:
    string buffer;
    MyXmlFormatTarget (const MyXmlFormatTarget & other) = delete;
    void operator=(const MyXmlFormatTarget & rhs) = delete;
};

class  SAX2ReadXpath
{
public:	
	SAX2ReadXpath( );
	SAX2ReadXpath( char iDelimiter );
	~SAX2ReadXpath();
	
	char* StartXpath( const char* psSubXpath );
	char* EndXpath( );
    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------    
	char* GetXPath( );
	int	  GetLevel( );
protected:
	char	fTagPath[MAX_XML_PATH_LENGTH];
	int		fTagLevel;
	char	fDelimiter;
};	

class SAX2ReadHandler : public DefaultHandler, public SAX2ReadXpath
{
public:
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
    SAX2ReadHandler();
    SAX2ReadHandler(const char* encoding_name,const XMLFormatter::UnRepFlags unRepFlags=XMLFormatter::UnRep_CharRef);
    ~SAX2ReadHandler();

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    inline bool getSawErrors() const {  return fSawErrors; }
    inline std::string getErrorsText() const { return fErrorText; };
    inline void setCallbackHandlers(XercesXmlHandle_t handlers) { m_callback = handlers; };

    // -----------------------------------------------------------------------
    //  Handlers for the SAX ContentHandler interface
    // -----------------------------------------------------------------------
    void startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const Attributes& attrs);
    void endElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname);
    void characters(const XMLCh* const chars, const XMLSize_t length);
    void ignorableWhitespace(const XMLCh* const chars, const XMLSize_t length);
    void startDocument();

    // -----------------------------------------------------------------------
    //  Handlers for the SAX ErrorHandler interface
    // -----------------------------------------------------------------------
	void warning(const SAXParseException& exc);
    void error(const SAXParseException& exc);
    void fatalError(const SAXParseException& exc);
    void resetErrors();
private:
    std::string fCurElement;
    std::string fErrorText;
    bool  fDoElementStart;
    bool  fSawErrors;

    XMLFormatter*    fFormatter;
    MyXmlFormatTarget*  fTarget;

    XercesXmlHandle_t m_callback;
    XercesXmlNode m_xmlNode;
};

class CSAX2Parser 
{
public:
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
	CSAX2Parser( );
	~CSAX2Parser( );
    // -----------------------------------------------------------------------
    //  Setter methods
    // -----------------------------------------------------------------------    
    void  SetXmlFile( const char* iXmlFile );
    void  SetNamespaces ( bool on = true);
    void  SetSchema ( bool on = true);
    void  SetSchemaFullChecking(bool on = true);
    void  SetSax2Handleres( XercesXmlHandle_t handlers);

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    inline std::string& GetErrors() { return fErrors; } ;
    // -----------------------------------------------------------------------
    //  Handlers for the SAX CSAX2Parser interface
    // -----------------------------------------------------------------------
    int	  ParseXmlFile( const string& encoding_name = XML_ENCODEING_DEFAULT);

    // -----------------------------------------------------------------------
    //  Static Handlers for the SAX CSAX2Parser interface
    // -----------------------------------------------------------------------  
    static void  SetLocalStr(const char* iLocalStr);
	static bool  Initialize( );
    static void  UnInitialize( );    

    static int XercescSchemaForXmlFile(const char* xmlFile, const char* xsdFile, char* error);
    static int XercescSchemaForXmlStr(const char* xmlStr,  const char* xsdFile, char* error);

private:
	int	  ParseXmlFile( SAX2XMLReader* pParser, SAX2ReadHandler* pHandler );
private:
	char  fXmlFile[MAX_XML_PATH_LENGTH];
    bool  fDoNamespaces;
    bool  fDoSchema;
    bool  fSchemaFullChecking;
    bool  fIdentityConstraintChecking;
    bool  fNamespacePrefixes;    
    std::string fErrors;

    XercesXmlHandle_t m_saxHandlers;

    static bool  fDoInitialize;
    static char  fLocalStr[60];
    static bool  fRecognizeNEL;    
};
